﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ThreadModule.Interface;
using MathNet.Numerics.LinearAlgebra;

namespace ThreadModule.Data
{
    /// <summary>
    /// 锥螺纹量规类
    /// </summary>
    public class TaperThreadGauge : ThreadBase, ITaperThread
    {
        public TaperThreadGauge(ref ThreadInformation threadInformation, double threshodAngle = 2.0, CalculationMethod calculationMethod = CalculationMethod.Iterator) : base(ref threadInformation, threshodAngle, calculationMethod)
        {
            this.Run(ref threadInformation);
        }

        public override void Run(ref ThreadInformation threadInformation)
        {
            ThreadParameters measuredThreadParameter = new ThreadParameters();

            CalculateThreadParameters(ref threadInformation, Position.Top);
            CalculateThreadParameters(ref threadInformation, Position.Bottom);

            measuredThreadParameter.isNominalValues = false;//为实测值
            var calculatedTopParameter = threadInformation.topCalculatedParameter;
            var calculatedBottomParameter = threadInformation.bottomCalculatedParameter;
            ShowCalculatedParameters(calculatedTopParameter);
            ShowCalculatedParameters(calculatedBottomParameter);

            measuredThreadParameter.pitchDiameter = ThreadBase.GetPitchDiameterValue(ref threadInformation);
            measuredThreadParameter.majorDiameter = ThreadBase.GetMajorDiameterValue(ref threadInformation);
            measuredThreadParameter.minorDiameter = ThreadBase.GetMinorDiameterValue(ref threadInformation);
            measuredThreadParameter.pitch = (calculatedBottomParameter.pitch + calculatedTopParameter.pitch) / 2;
            measuredThreadParameter.leftHalfAngle = (calculatedBottomParameter.leftHalfAngle + calculatedTopParameter.leftHalfAngle) / 2;
            measuredThreadParameter.rightHalfAngle = (calculatedBottomParameter.rightHalfAngle + calculatedTopParameter.rightHalfAngle) / 2;
            measuredThreadParameter.fullAngle = calculatedBottomParameter.fullAngle + calculatedTopParameter.fullAngle;
            measuredThreadParameter.taper = this.GetTaper(ref threadInformation);
            threadInformation.measuredThreadParameter = measuredThreadParameter;
            ShowOutputParameters(measuredThreadParameter);
        }

        protected override void CalculateThreadParameters(ref ThreadInformation threadInformation, Position position)
        {
            var taper = threadInformation.nominalThreadParameter.taper;

            CalculatedThreadParameter calculatedParameter = new CalculatedThreadParameter();
            calculatedParameter.position = position;

            #region 螺纹计算
            var listRawdata = new List<RawDataPoint>();
            switch (position)
            {
                case Position.Top:
                    listRawdata = threadInformation.topThreadRawData.listRawData;
                    break;
                case Position.Bottom:
                    listRawdata = threadInformation.bottomThreadRawData.listRawData;
                    break;
            }

            _toothHeight = CalculateToothHeight(threadInformation);

            double rotatedValue = 0.0;
            switch (position)
            {
                case Position.Top:
                    rotatedValue = (double)(-taper / 2);
                    break;
                case Position.Bottom:
                    rotatedValue = (double)(taper / 2);
                    break;
            }

            var listRotatedData = RotateDataPoints(listRawdata, rotatedValue);
            int firstIndex = FindFirstPointIndex(listRotatedData, GetAverage(listRawdata));
            int interval = (int)(threadInformation.nominalThreadParameter.pitch / _samplingDensity / 2);//按照半个螺距获取
            listExtremPointsIndex = FindExtremPointsIndex(listRawdata, firstIndex, interval);

            //计算大径小径
            //计算大径小径
            List<RawDataPoint> listFitMajorPoints;
            List<RawDataPoint> listFitMinorPoints;
            var paramMajor = FitMajorOrMinorLine(listRawdata, listExtremPointsIndex, out listFitMajorPoints, position, SearchMode.Major);
            var paramMinor = FitMajorOrMinorLine(listRawdata, listExtremPointsIndex, out listFitMinorPoints, position, SearchMode.Minor);

            //计算中径
            var paramPitchDiameter = CalculatePitchDiameter(listRawdata, paramMajor, position);

            var listPitch = CalculatePitch(paramPitchDiameter, _listFullAngleParameters, position);
            var listHalfAngle = CalculateHalfAngle(paramPitchDiameter, _listFullAngleParameters, position);

            calculatedParameter.listFullAngleParameters = _listFullAngleParameters;
            calculatedParameter.listPitch = listPitch;
            calculatedParameter.pitch = calculatedParameter.GetPitch();
            calculatedParameter.paramMajor = paramMajor;
            calculatedParameter.paramMinor = paramMinor;
            calculatedParameter.paramPitchDiameter = paramPitchDiameter;
            calculatedParameter.listHalfAngle = listHalfAngle;
            calculatedParameter.leftHalfAngle = calculatedParameter.GetHalfAngle(true);
            calculatedParameter.rightHalfAngle = calculatedParameter.GetHalfAngle(false);
            calculatedParameter.fullAngle = calculatedParameter.GetFullAngle();
            calculatedParameter.listFitMajorPoints = listFitMajorPoints;
            calculatedParameter.listFitMinorPoints = listFitMinorPoints;
            calculatedParameter.listFitAnglePoints = base._listFitAnglePoints;
            //ShowCalculatedParameters(calculatedParameter);

            switch (position)
            {
                case Position.Top:
                    threadInformation.topCalculatedParameter = calculatedParameter;
                    break;
                case Position.Bottom:
                    threadInformation.bottomCalculatedParameter = calculatedParameter;
                    break;
                default:
                    break;
            }
            #endregion
        }

        public double GetTaper(ref ThreadInformation threadInformation)
        {
            var topPitchDiameter = threadInformation.topCalculatedParameter.paramPitchDiameter;
            var bottomPitchDiameter = threadInformation.bottomCalculatedParameter.paramPitchDiameter;
            double taper = GetAngleBetweenLines(topPitchDiameter.Item2, bottomPitchDiameter.Item2);
            var value = FindAngleBisector(topPitchDiameter.Item2, topPitchDiameter.Item1, bottomPitchDiameter.Item2, bottomPitchDiameter.Item1);
            return AngleConverter.ConvertDegreeToRad(taper);
        }

        public List<RawDataPoint> RotateDataPoints(List<RawDataPoint> listRawdata, double rotatedAngle)
        {
            double angleInRad = AngleConverter.ConvertDegreeToRad(rotatedAngle);
            double[,] matrix = new double[2, 2] { { Math.Cos(angleInRad), -Math.Sin(angleInRad) }, { Math.Sin(angleInRad), Math.Cos(angleInRad) } };
            var rotateMatrix = Matrix<double>.Build.DenseOfArray(matrix);

            List<RawDataPoint> listRotatedData = new List<RawDataPoint>();
            for (int i = 0; i < listRawdata.Count; i++)
            {
                var value = listRawdata[i];
                var vecValue = Vector<double>.Build.DenseOfArray(new double[2] { value.X, value.Y });
                var rotatedValue = rotateMatrix * vecValue;
                listRotatedData.Add(new RawDataPoint() { X = rotatedValue[0], Y = rotatedValue[1] });
            }
            return listRotatedData;
        }

        public Tuple<double, double> FindAngleBisector(double k1, double b1, double k2, double b2)
        {
            double halfAngle = AngleConverter.GetAngleBetweenLines(k1, k2) / 2.0;
            var ptIntersection = base.GetIntersectPoint(k1, b1, k2, b2);

            // 计算两条直线的相对斜率（即 k2/ k1）
            double relativeSlope = Math.Tan(AngleConverter.ConvertDegreeToRad(halfAngle));
            double k = k1 * relativeSlope;
            // 计算未知直线的截距 b
            double b = ptIntersection.Y - k * ptIntersection.X + b1;
            return new Tuple<double, double>(b, k);
        }
    }
}
